﻿#Requires AutoHotkey v2.0

class CustomFs {
  __New(path) {
    if !FileExist(path)
      throw Error('文件不存在')
    this.path := path
    this.m := Map()
  }

  Init() {
    f := FileOpen(this.path, 'rw', 'utf-8')
    while lt := f.ReadLine() {
      if lt ~= '^#' ; comment
        continue
      if lt ~= '.*:$' { ; k : array
        list := Array()
        kl := SubStr(lt, 1, StrLen(lt) - 1)
        if this.m.Has(kl)
          throw Error('duplicate key -> ' kl)
        while (slt := f.ReadLine()) ~= '^-' {
          list.Push(LTrim(SubStr(slt, 2)))
          fpos := f.Pos
        }
        this.m.Set(kl, list)
        f.Pos := fpos
      } else { ; k : v
        kp := InStr(lt, ':')
        k := SubStr(lt, 1, kp - 1)
        if this.m.Has(k)
          throw Error('duplicate key -> ' k)
        v := SubStr(lt, kp + 1)
        this.m.Set(k, v)
      }
    }
    f.Close()
  }

  Update() {
    if this.m.Count = 0
      if MsgBox('empty data, continue?', , 0x1) != 'ok'
        return
    f := FileOpen(this.path, 'rw', 'utf-8')
    text := ''
    while lt := f.ReadLine() { ; update
      if lt ~= '^#' {
        text .= lt '`n'
        continue
      }
      if lt ~= '.*:$' { ; k : Array
        kl := SubStr(lt, 1, StrLen(lt) - 1)
        if !this.m.Has(kl) {
          while (slt := f.ReadLine()) ~= '^-'
            fpos := f.Pos
          f.Pos := fpos
          continue
        }
        text .= lt '`n'
        vl := this.m.Get(kl)
        for vi in vl {
          text .= '-' vi '`n'
        }
        this.m.Delete(kl)
      } else { ; k : v
        kp := InStr(lt, ':')
        k := SubStr(lt, 1, kp - 1)
        if !this.m.Has(k)
          continue
        v := this.m.Get(k)
        text .= k ':' v '`n'
        this.m.Delete(k)
      }
    }
    f.Close()
    ; override
    fw := FileOpen(this.path, 'w', 'utf-8')
    fw.Write(text)
    fw.Close()
    ; append
    fa := FileOpen(this.path, 'a', 'utf-8')
    for k, v in this.m {
      if v is Array {
        fa.Write(k ':`n')
        for vi in v
          fa.Write('-' vi '`n')
      } else
        fa.Write(k ':' v '`n')
    }
    fa.Close()
    ; reset
    this.m := Map()
    this.Init()
  }

  Set(k, v) {
    if not v is Array { ; replace key-value
      v := StrReplace(v, A_ScriptDir, 'A_ScriptDir')
      v := StrReplace(v, A_Desktop, 'A_Desktop')
    }
    this.m.Set(k, v)
  }

  Get(k) {
    if !this.m.Has(k) {
      MsgBox 'no result , key is :' k
      return
    }
    v := this.m.Get(k)
    if not v is Array {
      v := StrReplace(v, 'A_ScriptDir', A_ScriptDir)
      v := StrReplace(v, 'A_Desktop', A_Desktop)
    }
    return v
  }
}

if A_LineFile == A_ScriptFullPath {
  filePath := A_ScriptDir '\CustomFs_test.txt'
  cfs := CustomFs(filePath)
  cfs.Init()
  cfs.Set('cache', 'abc')
  cfs.Set('group', Array('default', 'newGroup'))
  cfs.Set('new', 'test')
  cfs.Update()
}